

module axis_recorder(
  input           CLK,
  input           RST,
  
//Register Port
  input           RP_EN,
  input   [3:0]   RP_WE,
  input   [2:0]   RP_A,
  input   [31:0]  RP_DI,
  output reg [31:0]  RP_DO,
  output reg      RP_RDY,
  
//  input   [47:0]  TAG,
  
  input           S_TVALID,
  output          S_TREADY,
  input   [127:0] S_TDATA,
  input   [3:0]   S_TSTRB,
  input   [47:0]  S_TUSER,
  input           S_TLAST,

  output  [0:0]   M_AWID,
  output  [31:0]  M_AWADDR,
  output  [7:0]   M_AWLEN,
  output  [2:0]   M_AWSIZE,
  output  [1:0]   M_AWBURST,
  output          M_AWVALID,
  input           M_AWREADY,
  output  [127:0] M_WDATA,
  output  [15:0]  M_WSTRB,
  output          M_WLAST,
  output          M_WVALID,
  input           M_WREADY,
  input   [0:0]   M_BID,
  input   [1:0]   M_BRESP,
  input           M_BVALID,
  output          M_BREADY
  );

  localparam
    IDLE = 0,
    WAIT_FOR_PACKET = 1,
    FLUSH = 2,
    PACKET_W = 3,
    DESCRIPTOR_W = 4,
    PAGE_AW = 5,
    WAIT_B = 6;
    
  reg [2:0] rState, sState;

  reg EnableRecording;
  reg PacketPending;
  reg DescriptorPending;
  reg [47:0] User;
  
  always @(posedge CLK)
  begin
    if (RST)
      rState <= IDLE;
    else
      rState <= sState;
  end
  
  always @*
  begin
    sState <= rState;
    case (rState)
      IDLE:
        if (EnableRecording)
          sState <= WAIT_FOR_PACKET;
      WAIT_FOR_PACKET:
        if (!EnableRecording)
          sState <= FLUSH;
        else if (S_TVALID)
          sState <= PACKET_W;
      FLUSH:
        if (M_WREADY && M_WLAST)
          sState <= PAGE_AW;
      PACKET_W:
        if (S_TVALID && M_WREADY)
        begin
          if (M_WLAST)
            sState <= PAGE_AW;
          else if (S_TLAST)
            sState <= DESCRIPTOR_W;
        end
      DESCRIPTOR_W:
        if (M_WREADY)
        begin
          if (M_WLAST)
            sState <= PAGE_AW;
          else if (!EnableRecording)
            sState <= FLUSH;
          else if(S_TVALID)
            sState <= PACKET_W;
          else
            sState <= WAIT_FOR_PACKET;
        end
      PAGE_AW:
        if (M_AWREADY)
        begin
          if (PacketPending)
            sState <= PACKET_W;
          else if (DescriptorPending)
            sState <= DESCRIPTOR_W;
          else if (EnableRecording)
          begin
            if(S_TVALID)
              sState <= PACKET_W;
            else
              sState <= WAIT_FOR_PACKET;
          end
          else
            sState <= WAIT_B;
        end
      WAIT_B:
        if (M_BVALID)
          sState <= IDLE;
    endcase
  end
  
  always @(posedge CLK)
  begin
    if (RST)
      PacketPending <= 1'b0;
    else if (S_TVALID && S_TREADY)
      PacketPending <= !S_TLAST;
  end
  
  always @(posedge CLK)
  begin
    if (RST)
      DescriptorPending <= 1'b0;
    else if (S_TVALID && S_TREADY && S_TLAST)
      DescriptorPending <= 1'b1;
    else if (rState == DESCRIPTOR_W)
      DescriptorPending <= 1'b0;
  end
  
  reg [31:12] PageAddr;
  reg [11:4] PageIndex;
  reg [15:0] rPacketLength;
  reg [31:0] rPacketAddr;
  reg [31:0] rDescriptorAddr;
  wire [127:0] DescriptorData = {User, rPacketLength, rPacketAddr, rDescriptorAddr};
  
  reg [31:12] StartPageAddr;
  reg [31:12] EndPageAddr;
  reg [31:0] InterruptMark = 32'h0;
  reg rFull, rEmpty;
  reg [31:0] PacketCount;

  always @(posedge CLK)
  begin
    if (rState == IDLE)
      PageAddr <= StartPageAddr;
    else if ((rState == PAGE_AW) && M_AWREADY)
    begin
      if (PageAddr == EndPageAddr)
        PageAddr <= StartPageAddr;
      else
        PageAddr <= PageAddr + 1;
    end
  end
  
  always @(posedge CLK)
  begin
    if (rState == IDLE)
      PageIndex <= 0;
    else if (M_WVALID && M_WREADY)
      PageIndex <= PageIndex + 1;
  end
  
  always @(posedge CLK)
  begin
    if (rState == IDLE)
      rPacketLength <= 16'b0;
    else if ((rState == DESCRIPTOR_W) && M_WREADY)
      rPacketLength <= 16'b0;
    else if (S_TVALID && S_TREADY)
    begin
      if (S_TLAST)
        rPacketLength <= rPacketLength + (
          (!S_TSTRB[1]) ? 4 :
          (!S_TSTRB[2]) ? 8 :
          (!S_TSTRB[3]) ? 12 : 16);
      else
        rPacketLength <= rPacketLength + 16;
    end
  end
    
  always @(posedge CLK)
  begin
    if ((rState == IDLE) && EnableRecording)
    begin
      rPacketAddr <= {StartPageAddr, 12'h0};
      rDescriptorAddr <= 32'b0;
    end
    else if ((rState == DESCRIPTOR_W) && M_WREADY)
    begin
      rPacketAddr <= {PageAddr, PageIndex, 4'b0} + 16;
      rDescriptorAddr <= {PageAddr, PageIndex, 4'b0};
    end
  end
  
  always @(posedge CLK)
  begin
    if (RST || ((rState == IDLE) && EnableRecording))
    begin
      rFull <= 1'b0;
      rEmpty <= 1'b1;
    end
    else if ((PageAddr == StartPageAddr) && (PageIndex == 0) && M_WVALID && M_WREADY)
    begin
      rEmpty <= 1'b0;
      if ((!rEmpty))
        rFull <= 1'b1;
    end
  end
  
  always @(posedge CLK)
  begin
    if ((rState == IDLE) && EnableRecording)
      PacketCount <= 00;
    else if ((rState == DESCRIPTOR_W) && M_WREADY)
      PacketCount <= PacketCount + 1;
  end
  
  always @(posedge CLK)
  begin
    if (S_TVALID && S_TREADY && S_TLAST)
      User <= S_TUSER;
  end
  
  always @(posedge CLK)
  begin
    if (RST || RP_RDY)
      RP_RDY <= 1'b0;
    else if(RP_EN)
      RP_RDY <= 1'b1;
  end
  
  
  always @(posedge CLK)
  begin
    if (RST)
    begin
      EnableRecording <= 1'b0;
    end
    else if (RP_EN)
    begin
      case (RP_A)
        3'h0:
          begin
            RP_DO <= {rFull, rEmpty, 11'h0, rState, 15'h0, EnableRecording};
            if (RP_WE[0]) EnableRecording <= RP_DI[0];
          end
        3'h1:
          begin
            RP_DO <= {StartPageAddr, 12'h0};
            if (RP_WE[1]) StartPageAddr[15:12] <= RP_DI[15:12];
            if (RP_WE[2]) StartPageAddr[23:16] <= RP_DI[23:16];
            if (RP_WE[3]) StartPageAddr[31:24] <= RP_DI[31:24];
          end 
        3'h2: RP_DO <= rDescriptorAddr;
        3'h3:
          begin
            RP_DO <= {EndPageAddr, 12'h0};
            if (RP_WE[1]) EndPageAddr[15:12] <= RP_DI[15:12];
            if (RP_WE[2]) EndPageAddr[23:16] <= RP_DI[23:16];
            if (RP_WE[3]) EndPageAddr[31:24] <= RP_DI[31:24];
          end
        3'h4:
          begin
            RP_DO <= InterruptMark;
            if (RP_WE[0]) InterruptMark[7:4] <= RP_DI[7:4];
            if (RP_WE[1]) InterruptMark[15:8] <= RP_DI[15:8];
            if (RP_WE[2]) InterruptMark[23:16] <= RP_DI[23:16];
            if (RP_WE[3]) InterruptMark[31:24] <= RP_DI[31:24];
          end
        3'h5: RP_DO <= PacketCount;
        default: RP_DO <= 32'h0;
      endcase
    end
  end
  
  assign S_TREADY = (rState == PACKET_W) && M_WREADY;
  assign M_AWID   = 1'b0;
  assign M_AWADDR = {PageAddr, 12'h0};
  assign M_AWLEN  =  8'hFF;
  assign M_AWSIZE = 3'b100;
  assign M_AWBURST = 2'b01;
  assign M_AWVALID = (rState == PAGE_AW); 
  assign M_WDATA  = (rState == DESCRIPTOR_W) ? DescriptorData :
                    (rState == PACKET_W) ? S_TDATA :
                    128'b0;
  assign M_WSTRB  = (rState == DESCRIPTOR_W) ? 16'hFFFF :
                    (rState == PACKET_W) ? {{4{S_TSTRB[3]}}, {4{S_TSTRB[2]}}, {4{S_TSTRB[1]}}, {4{S_TSTRB[0]}}} :
                    16'h0;
  assign M_WLAST =  & PageIndex;
  assign M_WVALID = ((rState == PACKET_W) && S_TVALID) || (rState == DESCRIPTOR_W) || (rState == FLUSH);
  assign M_BREADY = 1'b1;  
endmodule
